#include "module.h"
#include "lcd_font.h"
#include <stdlib.h>
#include <math.h>

void LCD_Init(void)
{
#if LCD_TYPE == LCD_ST7789
    bsp_lcd_st7789_init();
#else

#endif
}

void LCD_WriteData_16Bit(uint16_t data)
{
    bsp_lcd_write_data_16bit(data);
}

void LCD_SetWindows(uint16_t Start_X, uint16_t Start_Y,uint16_t End_X,uint16_t End_Y)
{
    bsp_lcd_setwindows(Start_X, Start_Y, End_X, End_Y);
}

// 设置光标
void LCD_SetCursor(uint16_t Xpos, uint16_t Ypos)
{
	LCD_SetWindows(Xpos,Ypos,Xpos,Ypos);	
}

void LCD_Clear(uint16_t Color)
{
    bsp_lcd_clear(Color);
}

void LCD_Fill(uint16_t Start_X, uint16_t Start_Y, uint16_t End_X, uint16_t End_Y, uint16_t Color)
{
    bsp_lcd_fill(Start_X, Start_Y, End_X, End_Y, Color);
}

void LCD_SetOrientation(uint8_t orientation)
{
    bsp_lcd_set_orientation(orientation);
}

/************************************************************************************************/


/**
 * @brief 显示单个字符
 * 
 * @param X 显示字符的起始 X 坐标，取值范围为 0 到屏幕宽度减去字符宽度
 * @param Y 显示字符的起始 Y 坐标，取值范围为 0 到屏幕高度减去字符高度
 * @param Char 要显示的字符，取值范围为标准 ASCII 字符集
 * @param cFont 字体颜色，可以使用 LCD_ColourTypeDef 枚举中的值
 * @param cBack 背景颜色，可以使用 LCD_ColourTypeDef 枚举中的值，若为 LCD_Colour_NONE 则不显示背景色
 * @param FontSize 字体大小，可以使用 LCD_FontSizeTypeDef 枚举中的值
 * 
 * @note cFont = 字体颜色，cBack = 背景颜色。若字体颜色不显示背景显示则为反色，反之则不显示背景色。
 */
void LCD_Show_Char(uint16_t X, uint16_t Y, char Char, 
                   LCD_ColourTypeDef cFont, LCD_ColourTypeDef cBack, 
                   LCD_FontSizeTypeDef FontSize) {
    uint8_t ASCII_EnCode = (uint8_t)Char - ' '; // 获取 ASCII 编码
    uint8_t Font_H = (FontSize >> 16); // 高度
    uint8_t Font_W = (FontSize & 0xFF); // 宽度
    uint8_t Font_Bytes_Per_Row = (Font_W + 7) / 8; // 每行占用的字节数
    const uint8_t* fontData;

    switch (FontSize) {
        case LCD_FontSize_12x6:
            fontData = LCD_Ascii12x6[ASCII_EnCode];
            break;
        case LCD_FontSize_16x8:
            fontData = LCD_Ascii16x8[ASCII_EnCode];
            break;
        case LCD_FontSize_20x10:
            fontData = LCD_Ascii20x10[ASCII_EnCode];
            break;
        default:
            return; // 不支持的字体大小，直接返回
    }

    for (uint8_t row = 0; row < Font_H; row++) {
        uint16_t rowData = 0;
        if (Font_Bytes_Per_Row == 2) {
            rowData = (fontData[row * 2 + 1] << 8) | fontData[row * 2];
        } else {
            rowData = fontData[row];
        }

        for (uint8_t bit = 0; bit < Font_W; bit++) {
            if ((rowData >> bit) & 0x01) {
                LCD_Draw_Dot(X + bit, Y + row, cFont);
            } else if (cBack != LCD_Colour_NONE) {
                LCD_Draw_Dot(X + bit, Y + row, cBack);
            }
        }
    }
}
									 
/**
 * @brief 显示字符串
 * 
 * @param X 起始 X 坐标，取值范围为 0 到屏幕宽度减去字符串宽度
 * @param Y 起始 Y 坐标，取值范围为 0 到屏幕高度减去字符串高度
 * @param str 要显示的字符串，取值范围为标准 ASCII 字符集
 * @param cFont 字体颜色，可以使用 LCD_ColourTypeDef 枚举中的值
 * @param cBack 背景颜色，可以使用 LCD_ColourTypeDef 枚举中的值，若为 LCD_Colour_NONE 则不显示背景色
 * @param FontSize 字体大小，可以使用 LCD_FontSizeTypeDef 枚举值
 */
void LCD_Show_String(uint16_t X, uint16_t Y, const char* str, 
                     LCD_ColourTypeDef cFont, LCD_ColourTypeDef cBack, 
                     LCD_FontSizeTypeDef FontSize) {
    uint16_t orig_X = X; // 保存原始 X 坐标
    uint8_t char_width = (FontSize & 0xFF); // 获取字体的宽度
    uint8_t char_height = (FontSize >> 16); // 获取字体的高度

    // 遍历字符串中的每个字符
    while (*str != '\0') {
        // 如果遇到换行符，跳到下一行
        if (*str == '\n') {
            X = orig_X; // 重置 X 坐标到起始位置
            Y += char_height; // 行高
            str++; // 移动到下一个字符
            continue;
        }

        // 显示单个字符
        LCD_Show_Char(X, Y, *str, cFont, cBack, FontSize);

        // 移动到下一个字符的位置
        X += char_width;

        // 移动到下一个字符
        str++;
    }
}

/**
 * @brief 显示 16 进制数
 * 
 * @param X 起始 X 坐标
 * @param Y 起始 Y 坐标
 * @param num 要显示的 16 进制数
 * @param cFont 字体颜色
 * @param cBack 背景颜色
 * @param FontSize 字体大小
 */
void LCD_Show_Hex(uint16_t X, uint16_t Y, uint32_t Hex,
                  LCD_ColourTypeDef cFont, LCD_ColourTypeDef cBack,
                  LCD_FontSizeTypeDef FontSize) {
    char str[12]; // 足够存储 32 位 16 进制数，包括终止符
    sprintf(str, "0x%X", (unsigned int)Hex); // 将 16 进制数转换为字符串
    LCD_Show_String(X, Y, str, cFont, cBack, FontSize); // 显示字符串
}

/**
 * @brief 显示二进制数
 * 
 * @param X 起始 X 坐标
 * @param Y 起始 Y 坐标
 * @param num 要显示的二进制数
 * @param cFont 字体颜色
 * @param cBack 背景颜色
 * @param FontSize 字体大小
 */
void LCD_Show_Bin(uint16_t X, uint16_t Y, uint32_t Bin, LCD_ColourTypeDef cFont, LCD_ColourTypeDef cBack, LCD_FontSizeTypeDef FontSize) {
    char str[35];  // 32 位二进制数 + "0b"前缀 + '\0'结束符
    str[0] = '0';
    str[1] = 'b';

    int startBit = 0;
    if (Bin <= 0xFF) {      // 8 位
        startBit = 7;
    } else if (Bin <= 0xFFFF) { // 16 位
        startBit = 15;
    } else {               // 32 位
        startBit = 31;
    }

    for (int i = 0; i <= startBit; i++) {
        str[2 + i] = (Bin & (1 << (startBit - i))) ? '1' : '0';
    }
    str[3 + startBit] = '\0';  // 结束符

    LCD_Show_String(X, Y, str, cFont, cBack, FontSize);
}

/**
 * @brief 显示双精度浮点数
 * 
 * @param X 起始 X 坐标
 * @param Y 起始 Y 坐标
 * @param num 要显示的双精度浮点数
 * @param cFont 字体颜色
 * @param cBack 背景颜色
 * @param FontSize 字体大小
 */
void LCD_Show_Float(uint16_t X, uint16_t Y, double Float,
                    LCD_ColourTypeDef cFont, LCD_ColourTypeDef cBack,
                    LCD_FontSizeTypeDef FontSize) {
    char str[32]; // 足够存储浮点数转换后的字符串，包括终止符
    sprintf(str, "%f", Float); // 将浮点数转换为字符串
    LCD_Show_String(X, Y, str, cFont, cBack, FontSize); // 显示字符串
}

/**
 * @brief 显示有符号整数
 * 
 * @param X 起始 X 坐标
 * @param Y 起始 Y 坐标
 * @param num 要显示的有符号整数
 * @param cFont 字体颜色
 * @param cBack 背景颜色
 * @param FontSize 字体大小
 */
void LCD_Show_sInt(uint16_t X, uint16_t Y, int32_t num,
                   LCD_ColourTypeDef cFont, LCD_ColourTypeDef cBack,
                   LCD_FontSizeTypeDef FontSize) {
    char str[13]; // 足够存储 32 位有符号整数和符号，包括终止符
    if (num >= 0) {
        // 如果是正数，手动添加前缀 "+"
        sprintf(str, "+%d", (unsigned int)num);
    } else {
        // 如果是负数，格式化字符串
        sprintf(str, "%d", (unsigned int)num);
    }
    LCD_Show_String(X, Y, str, cFont, cBack, FontSize); // 显示字符串
}

/**
 * @brief 显示无符号整数
 * 
 * @param X 起始 X 坐标
 * @param Y 起始 Y 坐标
 * @param num 要显示的无符号整数
 * @param cFont 字体颜色
 * @param cBack 背景颜色
 * @param FontSize 字体大小
 */
void LCD_Show_uInt(uint16_t X, uint16_t Y, uint32_t num,
                   LCD_ColourTypeDef cFont, LCD_ColourTypeDef cBack,
                   LCD_FontSizeTypeDef FontSize) {
    char str[12]; // 足够存储 32 位无符号整数，包括终止符
    sprintf(str, "%u", (unsigned int)num); // 将无符号整数转换为字符串
    LCD_Show_String(X, Y, str, cFont, cBack, FontSize); // 显示字符串
}



/************************************************************************************************/

/* 画点 */
void LCD_Draw_Dot(uint16_t x,uint16_t y,uint16_t Data)
{
	LCD_SetWindows(x,y,x,y);
	LCD_WriteData_16Bit(Data);
}


/**
 * @brief 绘制贝塞尔曲线
 * 
 * @param xPoints 贝塞尔曲线的 X 坐标数组
 * @param yPoints 贝塞尔曲线的 Y 坐标数组
 * @param numPoints 贝塞尔曲线的控制点数量
 * @param lColour 曲线颜色
 */
void LCD_Draw_Bezier(const uint16_t* xPoints, const uint16_t* yPoints, uint16_t numPoints, LCD_ColourTypeDef lColour) {
    if (numPoints < 4) return; // 至少需要四个点来绘制三阶贝塞尔曲线

    for (float t = 0; t <= 1; t += 0.01) {
        float x = pow(1 - t, 3) * xPoints[0] + 3 * pow(1 - t, 2) * t * xPoints[1] + 3 * (1 - t) * pow(t, 2) * xPoints[2] + pow(t, 3) * xPoints[3];
        float y = pow(1 - t, 3) * yPoints[0] + 3 * pow(1 - t, 2) * t * yPoints[1] + 3 * (1 - t) * pow(t, 2) * yPoints[2] + pow(t, 3) * yPoints[3];
        LCD_Draw_Dot((uint16_t)x, (uint16_t)y, lColour);
    }
}



/**
 * @brief 绘制多边形
 * 
 * @param xPoints 多边形的 X 坐标数组
 * @param yPoints 多边形的 Y 坐标数组
 * @param numPoints 多边形的顶点数量
 * @param lColour 边框颜色
 * @param fColour 填充颜色
 */
void LCD_Draw_Polygon(const uint16_t* xPoints, const uint16_t* yPoints, uint8_t numPoints, LCD_ColourTypeDef lColour, LCD_ColourTypeDef fColour) {
    if (numPoints < 3) return; // 至少需要三个点来绘制多边形

    // 填充多边形区域
    if (fColour != LCD_Colour_NONE) {
        int16_t minY = yPoints[0], maxY = yPoints[0];
        for (uint8_t i = 1; i < numPoints; i++) {
            if (yPoints[i] < minY) minY = yPoints[i];
            if (yPoints[i] > maxY) maxY = yPoints[i];
        }

        for (int16_t y = minY; y <= maxY; y++) {
            // 找到交点
            uint8_t intersections = 0;
            uint16_t nodes[10]; // 假设最多10个交点
            uint8_t j = numPoints - 1;
            for (uint8_t i = 0; i < numPoints; i++) {
                if ((yPoints[i] < y && yPoints[j] >= y) || (yPoints[j] < y && yPoints[i] >= y)) {
                    nodes[intersections++] = xPoints[i] + (y - yPoints[i]) * (xPoints[j] - xPoints[i]) / (yPoints[j] - yPoints[i]);
                }
                j = i;
            }

            // 排序交点
            for (uint8_t i = 1; i < intersections; i++) {
                uint8_t node = nodes[i];
                uint8_t k = i;
                while (k > 0 && nodes[k - 1] > node) {
                    nodes[k] = nodes[k - 1];
                    k--;
                }
                nodes[k] = node;
            }

            // 填充交点之间的像素
            for (uint8_t i = 0; i < intersections; i += 2) {
                if (nodes[i] >= 128) break; // 屏幕宽度为128
                if (nodes[i + 1] > 0) {
                    if (nodes[i] < 0) nodes[i] = 0;
                    if (nodes[i + 1] > 128) nodes[i + 1] = 128;
                    for (uint8_t x = nodes[i]; x < nodes[i + 1]; x++) {
                        LCD_Draw_Dot(x, y, fColour);
                    }
                }
            }
        }
    }

    // 绘制多边形边框
    for (uint8_t i = 0; i < numPoints; i++) {
        uint8_t j = (i + 1) % numPoints;
        LCD_Draw_Line(xPoints[i], yPoints[i], xPoints[j], yPoints[j], lColour);
    }
}

/**
 * @brief 填充多边形
 * 
 * @param xPoints 多边形的 X 坐标数组
 * @param yPoints 多边形的 Y 坐标数组
 * @param numPoints 多边形的顶点数量
 * @param fColour 填充颜色
 */
void LCD_Draw_FillPolygon(const uint16_t* xPoints, const uint16_t* yPoints, uint8_t numPoints, LCD_ColourTypeDef fColour) {
    LCD_Draw_Polygon(xPoints, yPoints, numPoints, fColour, fColour);
}

/**
 * @brief 绘制一条线
 * 
 * @param sX 起点的 X 坐标
 * @param sY 起点的 Y 坐标
 * @param eX 终点的 X 坐标
 * @param eY 终点的 Y 坐标
 * @param lColour 线的颜色
 */
void LCD_Draw_Line(uint16_t sX, uint16_t sY, uint16_t eX, uint16_t eY, LCD_ColourTypeDef lColour) {
    int dx = abs(eX - sX), sx = sX < eX ? 1 : -1;
    int dy = -abs(eY - sY), sy = sY < eY ? 1 : -1;
    int err = dx + dy, e2;

    while (1) {
        LCD_Draw_Dot(sX, sY, lColour); // 绘制点
        if (sX == eX && sY == eY) break; // 终止条件
        e2 = 2 * err;
        if (e2 >= dy) { err += dy; sX += sx; } // 调整误差和步进
        if (e2 <= dx) { err += dx; sY += sy; } // 调整误差和步进
    }
}

/**
 * @brief 绘制矩形
 * 
 * @param sX 左上角的 X 坐标
 * @param sY 左上角的 Y 坐标
 * @param eX 右下角的 X 坐标
 * @param eY 右下角的 Y 坐标
 * @param lColour 矩形边框颜色
 * @param fColour 矩形填充颜色，如果为 LCD_Colour_NONE 则不填充
 */
void LCD_Draw_Rectangle(uint16_t sX, uint16_t sY, uint16_t eX, uint16_t eY, LCD_ColourTypeDef lColour, LCD_ColourTypeDef fColour) {
    // 填充矩形区域
    if (fColour != LCD_Colour_NONE) {
        for (uint16_t y = sY; y <= eY; y++) {
            for (uint16_t x = sX; x <= eX; x++) {
                LCD_Draw_Dot(x, y, fColour);
            }
        }
    }

    // 绘制矩形边框
    LCD_Draw_Line(sX, sY, eX, sY, lColour); // Top
    LCD_Draw_Line(eX, sY, eX, eY, lColour); // Right
    LCD_Draw_Line(eX, eY, sX, eY, lColour); // Bottom
    LCD_Draw_Line(sX, eY, sX, sY, lColour); // Left
}


/**
 * @brief 绘制圆
 * 
 * @param sX 圆心的 X 坐标
 * @param sY 圆心的 Y 坐标
 * @param radius 圆的半径
 * @param lColour 圆的边框颜色
 * @param fColour 圆的填充颜色，如果为 LCD_Colour_NONE 则不填充
 */
void LCD_Draw_Round(uint16_t sX, uint16_t sY, uint16_t radius, LCD_ColourTypeDef lColour, LCD_ColourTypeDef fColour) {
    int16_t x = radius, y = 0, err = 0;

    // 填充圆区域
    if (fColour != LCD_Colour_NONE) {
        for (int16_t y = -radius; y <= radius; y++) {
            for (int16_t x = -radius; x <= radius; x++) {
                if (x * x + y * y <= radius * radius) {
                    LCD_Draw_Dot(sX + x, sY + y, fColour);
                }
            }
        }
    }

    // 绘制圆边框
    while (x >= y) {
        LCD_Draw_Dot(sX + x, sY + y, lColour);
        LCD_Draw_Dot(sX + y, sY + x, lColour);
        LCD_Draw_Dot(sX - y, sY + x, lColour);
        LCD_Draw_Dot(sX - x, sY + y, lColour);
        LCD_Draw_Dot(sX - x, sY - y, lColour);
        LCD_Draw_Dot(sX - y, sY - x, lColour);
        LCD_Draw_Dot(sX + y, sY - x, lColour);
        LCD_Draw_Dot(sX + x, sY - y, lColour);

        y++;
        if (err <= 0) {
            err += 2 * y + 1;
        } else {
            x--;
            err -= 2 * x + 1;
        }
    }
}


/**
 * @brief 绘制三角形
 * 
 * @param xPoints 三角形的 X 坐标数组
 * @param yPoints 三角形的 Y 坐标数组
 * @param lColour 边框颜色
 * @param fColour 填充颜色
 */
void LCD_Draw_Triangle(const uint16_t* xPoints, const uint16_t* yPoints, LCD_ColourTypeDef lColour, LCD_ColourTypeDef fColour) {
    if (fColour != LCD_Colour_NONE) {
        // 使用简单的扫描线算法填充三角形
        int16_t minY = yPoints[0], maxY = yPoints[0];
        for (uint8_t i = 1; i < 3; i++) {
            if (yPoints[i] < minY) minY = yPoints[i];
            if (yPoints[i] > maxY) maxY = yPoints[i];
        }

        for (int16_t y = minY; y <= maxY; y++) {
            // 找到交点
            uint8_t intersections = 0;
            uint16_t nodes[3]; // 最多3个交点
            uint8_t j = 2;
            for (uint8_t i = 0; i < 3; i++) {
                if ((yPoints[i] < y && yPoints[j] >= y) || (yPoints[j] < y && yPoints[i] >= y)) {
                    nodes[intersections++] = xPoints[i] + (y - yPoints[i]) * (xPoints[j] - xPoints[i]) / (yPoints[j] - yPoints[i]);
                }
                j = i;
            }

            // 排序交点
            if (intersections > 1 && nodes[0] > nodes[1]) SWAP(nodes[0], nodes[1]);

            // 填充交点之间的像素
            for (uint16_t x = nodes[0]; x < nodes[1]; x++) {
                LCD_Draw_Dot(x, y, fColour);
            }
        }
    }

    // 绘制三角形边框
    for (uint8_t i = 0; i < 3; i++) {
        uint8_t j = (i + 1) % 3;
        LCD_Draw_Line(xPoints[i], yPoints[i], xPoints[j], yPoints[j], lColour);
    }
}


/**
 * @brief 绘制椭圆
 * 
 * @param xc 椭圆中心的 X 坐标
 * @param yc 椭圆中心的 Y 坐标
 * @param width 椭圆的水平半轴长度
 * @param height 椭圆的垂直半轴长度
 * @param lColour 椭圆的边框颜色
 * @param fColour 椭圆的填充颜色，如果为 LCD_Colour_NONE 则不填充
 */
void LCD_Draw_Ellipse(uint16_t xc, uint16_t yc, uint16_t width, uint16_t height, LCD_ColourTypeDef lColour, LCD_ColourTypeDef fColour) {
    int a2 = width * width;
    int b2 = height * height;
    int fa2 = 4 * a2, fb2 = 4 * b2;
    int x, y, sigma;

    // 填充椭圆区域
    if (fColour != LCD_Colour_NONE) {
        for (y = -height; y <= height; y++) {
            for (x = -width; x <= width; x++) {
                if (x * x * b2 + y * y * a2 <= a2 * b2) {
                    LCD_Draw_Dot(xc + x, yc + y, fColour);
                }
            }
        }
    }

    // 绘制椭圆边框
    for (x = 0, y = height, sigma = 2 * b2 + a2 * (1 - 2 * height); b2 * x <= a2 * y; x++) {
        LCD_Draw_Dot(xc + x, yc + y, lColour);
        LCD_Draw_Dot(xc - x, yc + y, lColour);
        LCD_Draw_Dot(xc + x, yc - y, lColour);
        LCD_Draw_Dot(xc - x, yc - y, lColour);
        if (sigma >= 0) {
            sigma += fa2 * (1 - y);
            y--;
        }
        sigma += b2 * ((4 * x) + 6);
    }

    for (x = width, y = 0, sigma = 2 * a2 + b2 * (1 - 2 * width); a2 * y <= b2 * x; y++) {
        LCD_Draw_Dot(xc + x, yc + y, lColour);
        LCD_Draw_Dot(xc - x, yc + y, lColour);
        LCD_Draw_Dot(xc + x, yc - y, lColour);
        LCD_Draw_Dot(xc - x, yc - y, lColour);
        if (sigma >= 0) {
            sigma += fb2 * (1 - x);
            x--;
        }
        sigma += a2 * ((4 * y) + 6);
    }
}

/**
 * @brief 绘制弧
 * 
 * @param xc 圆心的 X 坐标
 * @param yc 圆心的 Y 坐标
 * @param radius 弧的半径
 * @param start_angle 起始角度 (度数)
 * @param end_angle 终止角度 (度数)
 * @param lColour 弧的颜色
 */
void LCD_Draw_Arc(uint16_t xc, uint16_t yc, uint16_t radius, uint8_t start_angle, uint8_t end_angle, LCD_ColourTypeDef lColour) {
    int x = radius * cos(start_angle * M_PI / 180);
    int y = radius * sin(start_angle * M_PI / 180);
    int x_end = radius * cos(end_angle * M_PI / 180);
    int y_end = radius * sin(end_angle * M_PI / 180);

    int dx, dy, e2, err, sx, sy;
    while (true) {
        LCD_Draw_Dot(xc + x, yc + y, lColour); // 绘制弧上的点
        if (x == x_end && y == y_end) break; // 终止条件
        dx = abs(x_end - x);
        dy = abs(y_end - y);
        sx = x < x_end ? 1 : -1;
        sy = y < y_end ? 1 : -1;
        err = dx - dy;
        e2 = 2 * err;
        if (e2 > -dy) { err -= dy; x += sx; }
        if (e2 < dx) { err += dx; y += sy; }
    }
}


/************************************************************************************************/



